/**
 * ****************************************************************************
 * Project: RoboBridge
 * ----------------------------------------------------------------------------
 * -------------------------- [c]2012 - KrawallMann ---------------------------
 * ---------------------- http://www.krawallmann.de.tc/ -----------------------
 * ****************************************************************************
 * File: AbstractLANCommunicationInterface.java Package:
 * tc.de.krawallmann.robobridge.communication Version: 1.0 Author(s):
 * KrawallMann
 * ****************************************************************************
 * Description:
 * ****************************************************************************
 */
package tc.de.krawallmann.robobridge.communication.lan;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;

import tc.de.krawallmann.robobridge.communication.AbstractCommunicationInterface;
import tc.de.krawallmann.robobridge.communication.ConnectionListener;
import tc.de.krawallmann.robobridge.communication.ThreadedReader;
import tc.de.krawallmann.robobridge.communication.Writer;
import tc.de.krawallmann.robobridge.util.Exceptions;
import tc.de.krawallmann.robobridge.util.Log;

/**
 * The Class AbstractLANCommunicationInterface represents a LAN communication
 * interface.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public abstract class AbstractLANCommunicationInterface<T extends LANRemoteDevice> extends
		AbstractCommunicationInterface<T> {

	private boolean connected;

	/** The connected device. */
	private transient T connectedDevice;

	/** The socket. */
	private transient Socket socket;

	/**
	 * Instantiates a new lAN communication interface.
	 * 
	 * @throws UnknownHostException
	 *             the unknown host exception
	 */
	public AbstractLANCommunicationInterface() throws UnknownHostException {
		super(new LANLocalDevice(), new ThreadedReader(), new Writer());
	}

	/*
	 * (non-Javadoc)
	 * @see communication.AbstractCommunicationInterface#close()
	 */
	@Override
	public void close(int reason) {
		if (!isConnected()) {
			throw new IllegalStateException("Not connected to any device!");
		}
		for (final ConnectionListener<T> l : listeners) {
			l.onDisconnect(connectedDevice, reason);
		}
		writer.close();
		reader.close();
		try {
			socket.close();
		} catch (final IOException exc) {
			Log.log(Level.WARNING, getClass(), "close", "Exception while closing - {0}!",
					exc.getMessage());
			// Nothing, just ignore
		}
		connected = false;
		setStartTime(-1);
	}

	/*
	 * (non-Javadoc)
	 * @see communication.AbstractCommunicationInterface#connect(communication.
	 * AbstractRemoteCommunicationDevice)
	 */
	@Override
	public void connect(final T device) throws IOException {
		if (device == null) {
			throw Exceptions.onNullPointer(this, "device");
		}
		if (isConnected()) {
			throw new IllegalStateException("Already connected!");
		}
		socket = new Socket();
		socket.connect(new InetSocketAddress(device.getAddress(), device.getPort()), 1000);
		if (!socket.isConnected()) {
			throw new IOException("Cannot connect to " + device.getName()
					+ ", it is not turned on or blocks the used port " + device.getPort() + "!");
		}
		connectedDevice = device;
		reader.connect(socket.getInputStream());
		writer.connect(socket.getOutputStream());
		for (final ConnectionListener<T> l : listeners) {
			l.onConnect(connectedDevice);
		}
		connected = true;
		setStartTime(System.currentTimeMillis());
	}

	/*
	 * (non-Javadoc)
	 * @see communication.AbstractCommunicationInterface#getConnectedDevice()
	 */
	@Override
	public T getConnectedDevice() {
		if (!isConnected()) {
			throw new IllegalStateException("Not connected to any device!");
		}
		return connectedDevice;
	}

	/*
	 * (non-Javadoc)
	 * @see communication.AbstractCommunicationInterface#isConnected()
	 */
	@Override
	public boolean isConnected() {
		return connected;
	}

}
